package DBside;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import tables.AbstractEntity;


import DBside.JDBCConnection.Table;
import entities.CD;
import entities.Track;

public class CDBox {
	private ArrayList<CD> cds;
	
	private HashMap<String , ArrayList<CD>> albums;
	private HashMap<String , ArrayList<CD>> artists;
	private HashMap<String , ArrayList<CD>> genres;
	
	//this one is different - String == song name
	private HashMap<String , ArrayList<Track>> songs; 
	
	
	public CDBox(){
		this.cds = new ArrayList<CD>();
		
		this.albums = new HashMap<String , ArrayList<CD>>();
		this.artists = new HashMap<String , ArrayList<CD>>();
		this.genres = new HashMap<String , ArrayList<CD>>();
		this.songs = new HashMap<String , ArrayList<Track>>();
	}
	
	
	
	//******* getters *********\\

	public ArrayList<CD> getCds() {
		return cds;
	}
	
	public HashMap<String, ArrayList<CD>> getAlbums() {
		return albums;
	}
	public HashMap<String, ArrayList<CD>> getArtists() {
		return artists;
	}
	
	public HashMap<String, ArrayList<CD>> getGenres() {
		return genres;
	}
	
	public HashMap<String, ArrayList<Track>> getSongs() {
		return songs;
	}
	
	public ArrayList<CD> getAllArtists(String artist){
		return getArtists().get(artist.toLowerCase());
	}
	
	//****** condition getters ********\\
	//notice: the condition getters generate the Strings so they'll be 
	//updated with the corresponding map and will be unique
	//if will generate these string (as fields) on the function addCD we will lose uniqueness
	
	public String[] getArtistsName() {
		return getArtists().keySet().toArray(new String[0]);
	}

	public String[] getAlbumsName() {
		return (String[]) getAlbums().keySet().toArray(new String[0]);
	}
	
	public String[] getGenresName() {
		return (String[]) getGenres().keySet().toArray(new String[0]);
	}
	
	public String[] getSongsName() {
		return (String[]) getSongs().keySet().toArray(new String[0]);
	}
	
	//******* Tools *********\\
	//inserting done only toLowerCase()
	public void addCD(CD cd){
		
		this.cds.add(cd);	
		//insert album
		ArrayList<CD> albumList = getAlbums().get(cd.getAlbum().getName());
		if(albumList == null){
			albumList = new ArrayList<CD>();
			albumList.add(cd);
			this.albums.put(cd.getAlbum().getName().toLowerCase(), albumList);
		}else{
			albumList.add(cd);
		}
		
		//insert Artist
		ArrayList<CD> artistList;
		for(List<Track> trackList : cd.getTracksMap().values()){
			//all of the tracks in trackList has the same artist
			artistList = getArtists().get(trackList.get(0).getArtist().getName().toLowerCase());
			if(artistList == null){
				//if artist not exist then so all of his songs don't
				artistList = new ArrayList<CD>();
				artistList.add(cd);
				this.artists.put(trackList.get(0).getArtist().getName().toLowerCase(), artistList);
			}else{
				artistList.add(cd);
			}
		}
		
		//insert Song
		ArrayList<Track> songList;
		for(Track track : cd.getTracks()){
			songList = this.getSongs().get(track.getSong().getName());
			if (songList == null){
				songList = new ArrayList<Track>();
				songList.add(track);
				getSongs().put(track.getSong().getName().toLowerCase(), songList);
			}else{
				songList.add(track);
			}
		}
		
		//insert Genre
		ArrayList<CD> genreList = getGenres().get(cd.getGenre().getName().toLowerCase());
		if(genreList == null){
			genreList = new ArrayList<CD>();
			genreList.add(cd);
			this.genres.put(cd.getGenre().getName().toLowerCase(), genreList);
		}else{
			genreList.add(cd);
		}	
	}
	
	/**
	 * fix only and all the ids that can be found in the DataBase
	 * the rest of the ids should be fixed on batching only since 
	 * we don't know the order of insertion which will reflect on the true id number 
	 * @param map
	 * @param resultSet: should be the result of 
	 * 			"resultSet = select("id, name", "WHERE " + albumsCondition, table);"
	 */
	public void updateIdsFromDataBase(Table table, ResultSet resultSet){
		if(Tools.isEmpty(resultSet)){
			return;
		}
		if(table.equals(Table.ARTIST)){
			ArrayList<CD> ListCDs;
			try {
				while(resultSet.next()){
					ListCDs = this.artists.get(resultSet.getString(2).toLowerCase());

					for(CD cd : ListCDs){
						cd.setAllArtistsId(resultSet.getString(2).toLowerCase(), resultSet.getInt(1));
						cd.setIsInDBToAllArtists(resultSet.getString(2).toLowerCase(), true);
					}
				}
			} catch (SQLException e) {
				Tools.printError("fixEntityId", e);
			}
		}else if(table.equals(Table.ALBUM)){
			ArrayList<CD> albumCDs;
			try {
				while(resultSet.next()){
					albumCDs = this.albums.get(resultSet.getString(2).toLowerCase());
									
					for(CD cd : albumCDs){
						cd.getAlbum().setIsInDB(true);
						cd.getAlbum().setId(resultSet.getInt(1));
					}
				}
				
			
			} catch (SQLException e) {
				Tools.printError("fixEntityId", e);
			}
		}else if(table.equals(Table.GENRE)){
			ArrayList<CD> genreCDs;
			try {
				while(resultSet.next()){
					genreCDs = this.genres.get(resultSet.getString(2).toLowerCase());
		
					for(CD cd : genreCDs){
						cd.getGenre().setIsInDB(true);
						cd.getGenre().setId(resultSet.getInt(1));
					}
				}
			} catch (SQLException e) {
				Tools.printError("fixEntityId", e);
			}
		}else if(table.equals(Table.SONG)){
			//must be done only after artist was called
			//only if both artist and song of the same track are in the db the song will get mark with inDB true
			ArrayList<Track> songsCDs;
			try {
				while(resultSet.next()){
					songsCDs = this.songs.get(resultSet.getString(2).toLowerCase());
					for(Track track : songsCDs){
						if(track.getArtist().isInDB()){
							//then artist and song already in DB then don't insert the song
							track.getSong().setIsInDB(true);
							track.getSong().setId(resultSet.getInt(1));
							track.getSong().setGenreId(resultSet.getInt(3));
						}
						//notice: if Artist is not in DB then we will insert the song with a new id and maybe a diffrent genre.
					}
				}
			} catch (SQLException e) {
				Tools.printError("fixEntityId", e);
			}
		}else{
			Tools.printError("fixEntityId: invalide table", null);
		}
	}
	
	/*
	 * no need to set the inDB since it's already set in prepareforinsert
	 */
	public int fixEntityId(AbstractEntity entity, Table table){		
		Boolean inDB = entity.getIsInDB();
		if(!entity.isValidId()){
			entity.fixId();
			//fix rest of the same name
			switch(table){
				case ALBUM:
					for(CD cd : getAlbums().get(entity.getName().toLowerCase())){
						cd.getAlbum().setId(entity.getId());
						cd.getAlbum().setIsInDB(true);
					}
				break;
				case GENRE:
					for(CD cd : getGenres().get(entity.getName().toLowerCase())){
						cd.getGenre().setId(entity.getId());
						cd.getGenre().setIsInDB(true);
					}
				break;
				case ARTIST: //TODO:
					for(CD cd : getArtists().get(entity.getName().toLowerCase())){
						cd.setAllArtistsId(entity.getName(), entity.getId());
						cd.setIsInDBToAllArtists(entity.getName(), true);
					}
				break;
				case SONG: //TODO:
					for(Track track : getSongs().get(entity.getName().toLowerCase())){
						track.getSong().setId(entity.getId());
						track.getSong().setIsInDB(true);
					}
				break;
				default:
					Tools.printError("fixEntityId", null);
					return -1;
			}
		}
		entity.setIsInDB(inDB);
		return entity.getId();
	}
	
	public String toString(){
		String result = "";
		for(CD cd : getCds()){
			result += "***CD:\n" + cd.toStringTest() + "\n";
		}	
		return result;
	}


	
	
	
}





















